﻿#include "Firework.h"

#include <cmath>
#include <iterator>
#include <algorithm>
#include <ranges>
#include <iterator>

#include <vsg/nodes/Switch.h>
#include <vsg/maths/transform.h>

#include "DataSingleton.h"
#include "utilities.h"

void Firework::init()
{
	m_fireworkParticles.resize(m_fireworkParticlesCount);
	initNode();
}

void Firework::update(const std::chrono::nanoseconds& delt) {
	if (m_state == FireworkState::Flying) {
		double g = 9.8;
		double delt_seconds = delt.count() * 1e-9;
		double dz = z_speed * delt_seconds + 0.5 * -g * pow(delt_seconds, 2.0);

		m_pos.z += dz;

		z_speed += -g * delt_seconds;

		m_matrixTransform->matrix = vsg::translate(m_pos);

		if (z_speed < 0) {
			m_state = FireworkState::Blast;
			m_switch->setAllChildren(false);
			initBombRandom(m_pos);
		}
	}

	if (m_state == FireworkState::Blast) {
		uint32_t particlesDoneCount = 0;
		for (uint32_t i{ 0 }; i < m_activateFireworkParticlesCount; ++i) {
			m_fireworkParticles[i].update(delt);
			if (m_fireworkParticles[i].isDone()) {
				particlesDoneCount++;
			}
		}
		if (particlesDoneCount >= m_activateFireworkParticlesCount - 1) {
			invalid();
		}
	}
}

vsg::ref_ptr<vsg::Node> Firework::getNode()
{
	auto scene = vsg::Group::create();
	scene->addChild(m_switch);
	for (auto& fireworkParticle : m_fireworkParticles) {
		scene->addChild(fireworkParticle.getNode());
	}
	return scene;
}

void Firework::addNewBombs(vsg::dvec3 pos, const std::vector<vsg::dvec3>& speedList, vsg::vec4 color, uint8_t lengthBomb) {
	if (m_activateFireworkParticlesCount >= m_fireworkParticlesCount) {
		return;
	}
	std::uniform_real_distribution<double> dragCoefficientDis(0.9, 0.99);
	double dragCoefficient = dragCoefficientDis(m_gen);
	uint32_t particleIndex = m_activateFireworkParticlesCount;
	for (int i{ 0 }; i < speedList.size() && particleIndex < m_fireworkParticlesCount; ++i) {
		m_fireworkParticles[particleIndex].setPos(pos);
		m_fireworkParticles[particleIndex].setSpeed(speedList[i]);
		m_fireworkParticles[particleIndex].setDragCoefficient(dragCoefficient);
		m_fireworkParticles[particleIndex].setTrailLength(lengthBomb);
		m_fireworkParticles[particleIndex].setColor(color);
		m_fireworkParticles[particleIndex].activate();
		particleIndex++;
	}
	setBombsMovePara(m_activateFireworkParticlesCount, particleIndex);
	m_activateFireworkParticlesCount = particleIndex;
}
void Firework::invalid()
{
	m_activateFireworkParticlesCount = 0;
	m_state = FireworkState::Done;
}
void Firework::initNode()
{
	m_switch = vsg::Switch::create();
	m_matrixTransform = vsg::MatrixTransform::create();
	vsg::GeometryInfo geoInfo;
	geoInfo.position = { 0.f, 0.f, 0.f };
	geoInfo.color = { 1.f, 0.f, 0.f, 1.f };
	geoInfo.transform = vsg::scale(1, 1, 5);
	vsg::StateInfo stateInfo;
	m_matrixTransform->addChild(DataSingleton::instance()->m_builder->createBox(geoInfo, stateInfo));
	m_switch->addChild(true, m_matrixTransform);
}
void Firework::setBombsMovePara(int begin, int end) {
	std::uniform_real_distribution<double> thetaLDis{ 1.8, 2.5 };
	std::uniform_real_distribution<double> ADis{ 2.0, 2.5 };

	double theta = thetaLDis(m_gen);
	double A = ADis(m_gen);

	for (int i = begin; i < end; ++i) {
		m_fireworkParticles[i].setMovePara(theta, A);
	}
}

void Firework::reset(vsg::dvec3 pos, double speed)
{
	m_pos = pos;
	z_speed = speed;
}

void Firework::activate() {
	m_state = FireworkState::Flying;
	m_matrixTransform->matrix = vsg::scale(1., 1., 1.);
	m_switch->setAllChildren(true);
}

void Firework::initBombRosebush(vsg::dvec3 pos) {
	vsg::dvec3 normal = getRandomNormal(-90, 90, 0, 360);
	std::uniform_real_distribution<double> radiusDis(50, 70);
	std::uniform_int_distribution<> bushCountDis(2, 30);
	std::vector<vsg::dvec3> rosebushPointsPos = getRosebushPos(m_pos, normal, radiusDis(m_gen), bushCountDis(m_gen));
	std::uniform_real_distribution<double> ratioDis(0.5, 0.7);
	std::vector<vsg::dvec3> speedList = getSpeedListFormPoslistWithRatio(m_pos, ratioDis(m_gen), rosebushPointsPos);
	vsg::vec4 color = getRandomColor();
	addNewBombs(m_pos, speedList, color, 1);
}

void Firework::initBombCircleAndInBall(vsg::dvec3 pos) {
	std::uniform_int_distribution<> lengthDis(3, 6);
	std::normal_distribution<double> speedDis(10, 5);
	std::uniform_real_distribution<double> radiusDis(-20, 20);
	std::uniform_int_distribution pointCountDis(-20, 20);

	double radius = 100 + radiusDis(m_gen);
	std::uniform_int_distribution circleCountDis(3, 20);
	uint8_t circleCount = circleCountDis(m_gen);
	double initSpeed = fabs(20 + speedDis(m_gen));
	uint8_t lengthBomb = lengthDis(m_gen);

	std::vector<vsg::dvec3> speedList;
	for (int i = 0; i < circleCount; ++i) {
		vsg::dvec3 normal = getRandomNormal(-90, 90, 0, 360);
		std::vector<vsg::dvec3> circlePointPos = getNormalCirclePos(m_pos, radius, normal, 0, 360, 30);
		std::vector<vsg::dvec3> tmpSpeedList = getSpeedlistFromPoslist(m_pos, initSpeed, circlePointPos);
		std::copy_n(tmpSpeedList.begin(), tmpSpeedList.size(), std::back_inserter(speedList));
	}
	vsg::vec4 color = getRandomColor();
	addNewBombs(m_pos, speedList, color, lengthBomb);

	std::uniform_int_distribution<> alphaCountDis(10, 30);
	int alphaCountRandom = alphaCountDis(m_gen);
	std::uniform_int_distribution<> betaCountDis(10, 30);
	int betaCountRandom = betaCountDis(m_gen);
	double speedRatio = 0.7;
	double iterRatio = 0.4;
	while (isToDo(iterRatio)) {
		color = getRandomColor();
		initSpeed = initSpeed * speedRatio;
		speedList.clear();;
		std::vector<vsg::dvec3> ballPointPosList = getBallPos(m_pos, 1, alphaCountRandom, betaCountRandom);
		for (vsg::dvec3& ballPointPos : ballPointPosList) {
			vsg::dvec3 direction = normalize(ballPointPos - m_pos);
			vsg::dvec3 speed = direction * initSpeed;
			speedList.push_back(speed);
		}
		addNewBombs(m_pos, speedList, color, lengthBomb);
	}
}

void Firework::initBombCircle(vsg::dvec3 pos) {
	std::normal_distribution<double> speedDis(10, 1);
	std::uniform_real_distribution<double> radiusDis(-20, 20);
	std::uniform_int_distribution pointCountDis(-20, 20);

	double radius = 100 + radiusDis(m_gen);
	vsg::dvec3 normal = getRandomNormal(-90, 90, 0, 360);
	double initSpeed = fabs(10 + speedDis(m_gen));
	std::uniform_int_distribution<> lengthDis(3, 6);
	uint8_t lengthBomb = lengthDis(m_gen);
	std::vector<vsg::dvec3> circlePointPos = getNormalCirclePos(m_pos, radius, normal, 0, 360, 30 + pointCountDis(m_gen));
	std::vector<vsg::dvec3> speedList = getSpeedlistFromPoslist(m_pos, initSpeed, circlePointPos);
	vsg::vec4 color = getRandomColor();
	addNewBombs(m_pos, speedList, color, lengthBomb);
}

void Firework::initBombCircles(vsg::dvec3 pos) {
	std::normal_distribution<double> speedDis(5, 0.5);
	std::uniform_real_distribution<double> radiusDis(-20, 20);
	std::uniform_int_distribution pointCountDis(-20, 20);

	double radius = 100 + radiusDis(m_gen);
	vsg::dvec3 normal = getRandomNormal(-90, 90, 0, 360);
	double initSpeed = fabs(speedDis(m_gen));
	std::uniform_int_distribution<> lengthDis(3, 6);
	uint8_t lengthBomb = lengthDis(m_gen);
	std::vector<vsg::dvec3> circlePointPos = getNormalCirclePos(m_pos, radius, normal, 0, 360, 10 + pointCountDis(m_gen));
	std::vector<vsg::dvec3> speedList = getSpeedlistFromPoslist(m_pos, initSpeed, circlePointPos);

	double speedRatio = 1.3;
	double iterRatio = 0.1;
	std::uniform_int_distribution pointNewCountDis(-10, 10);
	double iterCount = 0;
	while (isToDo(iterRatio) || iterCount < 4) {
		iterCount++;
		//		color = getRandomColor();
		initSpeed *= speedRatio;
		std::vector<vsg::dvec3> circlePointPos = getNormalCirclePos(m_pos, radius, normal, 0, 360, 20 + pointNewCountDis(m_gen) + (int)iterCount * 10);
		std::vector<vsg::dvec3> tmpSpeedList = getSpeedlistFromPoslist(m_pos, initSpeed, circlePointPos);
		//		addNewBombs(pos, speedList, color, lengthBomb);
		std::copy_n(tmpSpeedList.begin(), tmpSpeedList.size(), std::back_inserter(speedList));

		if (iterCount > 8) break;
	}
	vsg::vec4 color = getRandomColor();
	addNewBombs(m_pos, speedList, color, lengthBomb);
}

void Firework::initBombBall(vsg::dvec3 pos) {
	std::uniform_int_distribution<> alphaCountDis(10, 30);
	int alphaCountRandom = alphaCountDis(m_gen);
	std::uniform_int_distribution<> betaCountDis(10, 30);
	int betaCountRandom = betaCountDis(m_gen);
	std::vector<vsg::dvec3> ballPointPosList = getBallPos(m_pos, 1, alphaCountRandom, betaCountRandom);
	std::normal_distribution<double> speedDis(30, 5);
	double initSpeed = fabs(speedDis(m_gen));
	std::vector<vsg::dvec3> speedList = getSpeedlistFromPoslist(m_pos, initSpeed, ballPointPosList);

	std::uniform_int_distribution<> lengthDis(3, 6);
	uint8_t lengthBomb = lengthDis(m_gen);
	vsg::vec4 color = getRandomColor();
	addNewBombs(m_pos, speedList, color, lengthBomb);

	double speedRatio = 0.7;
	while (isToDo(0.5)) {
		color = getRandomColor();
		initSpeed = initSpeed * speedRatio;
		alphaCountRandom = alphaCountDis(m_gen);
		int betaCountRandom = betaCountDis(m_gen);
		ballPointPosList = getBallPos(m_pos, 1, alphaCountRandom, betaCountRandom);
		speedList = getSpeedlistFromPoslist(m_pos, initSpeed, ballPointPosList);
		addNewBombs(m_pos, speedList, color, lengthBomb);
	}
}

void Firework::initBombRandom(vsg::dvec3 pos) {
	double maxValue = 10;
	std::uniform_real_distribution<double> bombDis(0, maxValue);
	double bombValue = bombDis(m_gen);
	double classCount = 8;
	double deltValue = maxValue / classCount;
	double valueNow = deltValue;
	if (bombValue < valueNow) {
		initBombBall(pos);
		return;
	}
	valueNow += deltValue;
	if (bombValue < valueNow) {
		initBombCircles(pos);
		return;
	}
	valueNow += deltValue;
	if (bombValue < valueNow) {
		initBombRosebush(pos);
		return;
	}
	valueNow += deltValue;
	if (bombValue < valueNow) {
		initBombCircleAndInBall(pos);
		return;
	}
	valueNow += deltValue;
	if (bombValue < valueNow) {
		initBombLines(pos);
		return;
	}
	valueNow += deltValue;
	if (bombValue < valueNow) {
		initBombLittleLines(pos);
		return;
	}
	valueNow += deltValue;
	if (bombValue < valueNow) {
		initBombLinesAndInBall(pos);
		return;
	}
	valueNow += deltValue;
	if (bombValue <= maxValue) {
		initBombCone(pos);
		return;
	}
}

void Firework::initBombLittleLines(vsg::dvec3 pos) {
	std::uniform_int_distribution<> lineCountDis(865, 1085);
	std::normal_distribution<double> speedDis(30, 5);
	std::uniform_int_distribution<> lengthDis(10, 15);

	uint8_t lineCount = lineCountDis(m_gen);
	std::vector<vsg::dvec3> speedList;
	for (int i = 0; i < lineCount; ++i) {
		double initSpeed = 60 + speedDis(m_gen);
		vsg::dvec3 normal = getRandomNormal(45, 90, 0, 360);
		vsg::dvec3 speed = normal * initSpeed;
		speedList.push_back(speed);
	}
	vsg::vec4 color = getRandomColor();
	addNewBombs(pos, speedList, color, lengthDis(m_gen));
}

void Firework::initBombLinesAndInBall(vsg::dvec3 pos) {
	std::uniform_int_distribution<> lineCountDis(40, 120);
	std::normal_distribution<double> speedDis(10, 1);
	std::uniform_int_distribution<> lengthDis(3, 6);

	uint8_t lineCount = lineCountDis(m_gen);
	uint8_t lengthBomb = lengthDis(m_gen);
	std::vector<vsg::dvec3> speedList;
	double initSpeed = 40 + speedDis(m_gen);
	for (int i = 0; i < lineCount; ++i) {
		initSpeed = 40 + speedDis(m_gen);
		vsg::dvec3 normal = getRandomNormal(-90, 90, 0, 360);
		vsg::dvec3 speed = normal * initSpeed;
		speedList.push_back(speed);
	}
	vsg::vec4 color = getRandomColor();
	addNewBombs(m_pos, speedList, color, lengthBomb);

	std::uniform_int_distribution<> alphaCountDis(10, 30);
	int alphaCountRandom = alphaCountDis(m_gen);
	std::uniform_int_distribution<> betaCountDis(10, 30);
	int betaCountRandom = betaCountDis(m_gen);
	double speedRatio = 0.7;
	double iterRatio = 0.4;
	while (isToDo(iterRatio)) {
		color = getRandomColor();
		initSpeed = initSpeed * speedRatio;
		speedList.clear();;
		std::vector<vsg::dvec3> ballPointPosList = getBallPos(m_pos, 1, alphaCountRandom, betaCountRandom);
		for (vsg::dvec3& ballPointPos : ballPointPosList) {
			vsg::dvec3 direction = normalize(ballPointPos - m_pos);
			vsg::dvec3 speed = direction * initSpeed;
			speedList.push_back(speed);
		}
		addNewBombs(m_pos, speedList, color, lengthBomb);
	}
}

void Firework::initBombLines(vsg::dvec3 pos) {
	std::uniform_int_distribution<> lineCountDis(40, 120);
	std::normal_distribution<double> speedDis(20, 5);
	std::uniform_int_distribution<> lengthDis(3, 6);

	uint8_t lineCount = lineCountDis(m_gen);
	double initSpeed;
	std::vector<vsg::dvec3> speedList;
	for (int i = 0; i < lineCount; ++i) {
		initSpeed = 10 + std::fabs(speedDis(m_gen));
		vsg::dvec3 normal = getRandomNormal(-90, 90, 0, 360);
		vsg::dvec3 speed = normal * initSpeed;
		speedList.push_back(speed);
	}
	vsg::vec4 color = getRandomColor();
	addNewBombs(m_pos, speedList, color, lengthDis(m_gen));

	double speedRatio = 0.7;
	double iterRatio = 0.5;
	std::uniform_real_distribution<double> speedNewDis(-1, 1);
	double iterCount = 0;
	while (isToDo(iterRatio)) {
		iterCount++;
		speedList.clear();

		color = getRandomColor();
		lineCount = lineCountDis(m_gen);
		initSpeed = initSpeed * speedRatio;
		for (int i = 0; i < lineCount; ++i) {
			double speedRandom = initSpeed + speedNewDis(m_gen) * pow(speedRatio, iterCount);
			vsg::dvec3 normal = getRandomNormal(-90, 90, 0, 360);
			vsg::dvec3 speed = normal * speedRandom;
			speedList.push_back(speed);
		}
		addNewBombs(m_pos, speedList, color, 3 + lengthDis(m_gen));
	}
}

void Firework::initBombCone(vsg::dvec3 pos) {
	std::uniform_int_distribution<> lengthDis(3, 6);
	std::uniform_real_distribution<double> speedDis(20, 40);
	std::uniform_real_distribution<double> heightDis(50, 70);

	vsg::dvec3 normal = getRandomNormal(60, 90, 0, 360);
	double height = heightDis(m_gen);
	vsg::dvec3 vertexPos = m_pos + normal * height;
	std::vector<vsg::dvec3> conePointList = getNormalConePos(m_pos, vertexPos, height);
	double initSpeed = speedDis(m_gen);
	std::vector<vsg::dvec3> speedList = getSpeedlistFromPoslist(m_pos, initSpeed, conePointList);
	vsg::vec4 color = getRandomColor();
	addNewBombs(m_pos, speedList, color, lengthDis(m_gen));
}
